package com.bytenebula.asteroid;

import com.bytenebula.asteroid.entity.MetaDatabase;
import com.bytenebula.asteroid.entity.BnJdbcDriver;
import com.bytenebula.asteroid.service.NbJdbcDriverService;
import com.bytenebula.asteroid.utils.JdbcBuilder;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.sql.*;
import java.util.HashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

class DriverShim implements Driver {
    private final Driver driver;

    public DriverShim(Driver d) {
        this.driver = d;
    }

    public boolean acceptsURL(String u) throws SQLException {
        return this.driver.acceptsURL(u);
    }

    public Connection connect(String u, Properties p) throws SQLException {
        return this.driver.connect(u, p);
    }

    public int getMajorVersion() {
        return this.driver.getMajorVersion();
    }

    public int getMinorVersion() {
        return this.driver.getMinorVersion();
    }

    public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
        return this.driver.getPropertyInfo(u, p);
    }

    public boolean jdbcCompliant() {
        return this.driver.jdbcCompliant();
    }

    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return this.driver.getParentLogger();
    }
}

@Component
public class DynamicJdbcDriverLoaderService {

    // 缓存对应的Driver对象
    private static final Map<String, Driver> drivers = new HashMap<>();

    // 缓存所有可用JDBC驱动
    private static Map<String, BnJdbcDriver> driverConfigs = new HashMap<>();

    @Autowired
    public void setDriverService(NbJdbcDriverService driverService) {
        DynamicJdbcDriverLoaderService.driverService = driverService;
    }
    private static NbJdbcDriverService driverService;

    /**
     * 加载指定路径的JDBC Jar文件并注册驱动
     * @param jdbcPath JDBC Jar文件路径
     * @param driverClassName 驱动名称
     * @return 返回Driver对象，用于后续数据库驱动注册
     */
    public static Driver loadJdbcDriver(String jdbcPath, String driverClassName)
            throws MalformedURLException, ClassNotFoundException, SQLException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        String key = jdbcPath + "@" + driverClassName;
        Driver driver = null;
        if (!drivers.containsKey(key)) {
            URL url = DynamicJdbcDriverLoaderService.class.getResource(jdbcPath);
            URLClassLoader classLoader = new URLClassLoader(new URL[] {url});
            Class<?> driverClass = classLoader.loadClass(driverClassName);
            driver = (Driver) driverClass.getDeclaredConstructor().newInstance();
            Class.forName(driverClassName, true, classLoader);
            DriverManager.registerDriver(new DriverShim(driver));
            drivers.put(key, driver);
        }

        if (driver == null)
            driver = drivers.get(key);

        return driver;
    }

    public static HikariDataSource createDataSource(MetaDatabase database)
            throws MalformedURLException, SQLException, ClassNotFoundException, InvocationTargetException,
            NoSuchMethodException, InstantiationException, IllegalAccessException {

        // 第一次初始化所有可用JDBC驱动
        if (driverConfigs.isEmpty()) {
            driverConfigs = driverService.list().stream().collect(Collectors.toMap(BnJdbcDriver::getDriverType, Function.identity()));
        }

        if (database != null) {
            if (!driverConfigs.containsKey(database.getDbType()))
                throw new ByteNebulaException(ResultCodes.INVALID_DB_TYPE, database.getDbType());
            BnJdbcDriver jdbcDriver = driverConfigs.get(database.getDbType());
            Driver driver = DynamicJdbcDriverLoaderService
                    .loadJdbcDriver(Paths.get(Consts.JDBC_DRIVER_ROOT_PATH, jdbcDriver.getDriverPath()).toString(),
                            jdbcDriver.getClassName());
            if (driver != null) {
                String jdbc_url = JdbcBuilder.buildJdbcUrl(
                        database.getDbType(),
                        jdbcDriver.getUrlTemplate(),
                        jdbcDriver.getUrlSplitStr(),
                        database.getHostName(),
                        database.getPort(),
                        database.getDbName(),
                        database.getJdbcParams());

                HikariConfig config = new HikariConfig();
                config.setJdbcUrl(jdbc_url);
                config.setUsername(database.getUserName());
                config.setPassword(database.getPassword());
                config.setPoolName(database.getDbName());
                return new HikariDataSource(config);
            }
        }
        return null;
    }
}
